
package ubi3d.Data;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;

/**
 * Thread to supply the GUI with camera orientation data
 *

   Copyright 2009 BBC Research, David Lewis, <david.lewis4@bbc.co.uk>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */
public class CamThread extends Thread {

    private String address;     //Database address
    private Connection dbc;     //Database connection
    private ArrayList<float[]> lastTen = new ArrayList<float[]>();             //Array:{p,t,r,f}
    private String dbName;      //Database table name
    private DataSource parent;  //Parent to return data to
    private boolean run = true;

    @Override
    public void run() {
        try {

            /** This bit is the setup for the loop **/
            Class.forName("com.mysql.jdbc.Driver").newInstance();               //Load DB driver

            dbc = DriverManager.getConnection(address, "root", "test");    //Connect to DB

            Statement st = dbc.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); //Set up DB query

            ResultSet rs = st.executeQuery("select * from " + dbName + "cam");       //Initial query for first line (start timecode)

            if (!rs.next()) {
                throw new SQLException();                           //Bail out if there were no rows returned
            }


            //Wait for the tag database to get up to speed
            while (parent.firstTC == -1) {
                CamThread.sleep(10);
            }

            //Grab the starting time code (/20 because 40ms/frame, each result is a field)
            int currFrame = Math.round((parent.currentTc - parent.firstTC) / 20);
            int prevFrame = currFrame;
            int dbFrame = rs.getInt("frame");
            int firstdbFrame = dbFrame;

            /** This bit is the main timing loop **/
            while (rs.next()) {
                //Work out which frame the tag database is on
                currFrame = Math.round((parent.currentTc - parent.firstTC) / 20);
                //Get which frame this camera database is on
                dbFrame = rs.getInt("frame");
                ubi3d.Main.gui.updateFrame(dbFrame);

                //Check if the tag database frame has changed
                if (prevFrame != currFrame) {
                    if (dbFrame - firstdbFrame != currFrame) { //If it has, check it's not gone out of sync
                        rs.relative(currFrame - (dbFrame - firstdbFrame)); //Skip forward or back by the number of frames it's off to correct
                    }
                }
                prevFrame = currFrame;


                if (true) {   //Camera filtering test

                    float[] unfiltered = new float[]{rs.getFloat("pan"), rs.getFloat("tilt"), rs.getFloat("roll"), rs.getFloat("fov")};

                    lastTen.add(unfiltered);

                    float avgP = 0;
                    float avgT = 0;
                    float avgR = 0;
                    float avgF = 0;

                    for (int i = 0; i < lastTen.size(); i++) {
                        avgP += lastTen.get(i)[0];
                        avgT += lastTen.get(i)[1];
                        avgR += lastTen.get(i)[2];
                        avgF += lastTen.get(i)[3];
                    }
                    avgP = avgP / lastTen.size();
                    avgT = avgT / lastTen.size();
                    avgR = avgR / lastTen.size();
                    avgF = avgF / lastTen.size();


                    float[] direction = new float[]{avgP, avgT, avgR, avgF};
                    ubi3d.Main.gui.updateCamera(direction);

                    if (lastTen.size() > 9) {
                        lastTen.remove(0);
                    }

                } else {

                    //Get direction from DB row
                    float[] direction = {rs.getFloat("pan"), rs.getFloat("tilt"), rs.getFloat("roll"), rs.getFloat("fov")};
                    ubi3d.Main.gui.updateCamera(direction);
                }

                Thread.sleep(19); //Sleep for the duration of one frame

            }

            /** This bit is the error handling **/
        } catch (InterruptedException ex) {

            run = false;
            if (dbc != null) {
                try {
                    dbc.close();
                } catch (SQLException ex1) {
                    return;
                }
            }
        } catch (ClassNotFoundException ex) {
            System.out.println("DB driver fail");
        } catch (InstantiationException ex) {
            System.out.println("DB driver start fail");
        } catch (IllegalAccessException ex) {
            System.out.println("Illegal Access fail");
        } catch (SQLException ex) {
            System.out.println("SQL fail " + ex.getMessage());
            run = false;
            return;
        }
        /** End error handling **/
    }

    /**
     * Constructor
     * @param address   Database address e.g. jdbc:mysql://localhost:3306/events
     * @param parent    DataSource which created the thread (may be used to return results - not implemented)
     * @param table     Database table
     */
    public CamThread(String address, DataSource parent, String table) {
        this.address = address;
        this.parent = parent;
        this.dbName = table;
    }
}
